`timescale 1ns/1ps
`include "can_level_bit.v"
module tb_can_level_bit();
    // 时钟和复位信号
    reg         clk;
    reg         rstn;
    
    // CAN 接口信号
    reg         can_rx;
    wire        can_tx;
    
    // 用户接口信号
    wire        req;
    wire        rbit;
    reg         tbit;
    
    // 测试控制变量
    reg [7:0]   test_phase;
    integer     error_count;
    
    // 实例化被测模块
    can_level_bit #(
        .default_c_PTS(16'd34),
        .default_c_PBS1(16'd5),
        .default_c_PBS2(16'd10)
    ) u_dut (
        .rstn(rstn),
        .clk(clk),
        .can_rx(can_rx),
        .can_tx(can_tx),
        .req(req),
        .rbit(rbit),
        .tbit(tbit)
    );
    
    // 时钟生成 - 50MHz
    initial begin
        clk = 0;
        forever #10 clk = ~clk; // 20ns周期 = 50MHz
    end
    
    // 测试序列
    initial begin
        // 初始化
        initialize();
        
        // 测试1: 复位测试
        test_reset();
        
        // 测试2: 帧开始检测测试
        test_frame_start();
        
        // 测试3: 完整帧传输测试
        test_complete_frame();
        
        // 测试4: 位填充测试
        test_bit_stuffing();
        
        // 结束测试
        finish_test();
    end
    
    // 初始化任务
    task initialize;
        begin
            rstn = 0;
            can_rx = 1;
            tbit = 1;
            test_phase = 0;
            error_count = 0;
            
            // 保持复位状态
            repeat(10) @(posedge clk);
            
            // 释放复位
            rstn = 1;
            repeat(5) @(posedge clk);
            
            $display("[%0t] Testbench初始化完成", $time);
        end
    endtask
    
    // 复位测试
    task test_reset;
        begin
            test_phase = 1;
            $display("[%0t] 开始测试1: 复位测试", $time);
            
            // 验证复位后输出状态
            if (can_tx !== 1'b1) begin
                $error("复位后can_tx不为1");
                error_count = error_count + 1;
            end
            
            if (req !== 1'b0) begin
                $error("复位后req不为0");
                error_count = error_count + 1;
            end
            
            $display("[%0t] 复位测试完成", $time);
        end
    endtask
    
    // 帧开始检测测试
    task test_frame_start;
        begin
            test_phase = 2;
            $display("[%0t] 开始测试2: 帧开始检测测试", $time);
            
            // 生成帧开始（下降沿）
            @(posedge clk);
            can_rx = 1'b0;  // 帧开始位
            
            // 等待模块检测到帧开始
            wait( u_dut.inframe == 1'b1 );
            $display("[%0t] 帧开始检测成功", $time);
            
            // 恢复高电平
            repeat(10) @(posedge clk);
            can_rx = 1'b1;
            
            repeat(50) @(posedge clk);
        end
    endtask
    
    // 完整帧传输测试
    task test_complete_frame;
        reg [7:0] tx_data;
        reg [7:0] rx_data;
        integer i;
        begin
            test_phase = 3;
            $display("[%0t] 开始测试3: 完整帧传输测试", $time);
            
            tx_data = 8'hA5;
            
            // 发送帧开始位
            @(posedge clk);
            can_rx = 1'b0;
            
            // 等待进入帧内状态
            wait( u_dut.inframe == 1'b1 );
            
            // 发送数据位
            for (i = 0; i < 8; i = i + 1) begin
                // 等待采样请求
                wait( req == 1'b1 );
                @(posedge clk);
                
                // 设置要发送的位
                tbit = tx_data[i];
                
                // 在适当的时候设置RX位
                if (u_dut.stat == u_dut.STAT_PBS1) begin
                    can_rx = ~tx_data[i]; // 发送相反的位来测试
                end
                
                @(posedge clk);
            end
            
            // 发送帧结束（连续8个1）
            for (i = 0; i < 8; i = i + 1) begin
                wait( req == 1'b1 );
                @(posedge clk);
                tbit = 1'b1;
                can_rx = 1'b1;
                @(posedge clk);
            end
            
            // 等待帧结束
            wait( u_dut.inframe == 1'b0 );
            $display("[%0t] 完整帧传输测试完成", $time);
            
            repeat(100) @(posedge clk);
        end
    endtask
    
    // 位填充测试
    task test_bit_stuffing;
        integer i;
        begin
            test_phase = 4;
            $display("[%0t] 开始测试4: 位填充测试", $time);
            
            // 发送帧开始位
            @(posedge clk);
            can_rx = 1'b0;
            
            wait( u_dut.inframe == 1'b1 );
            
            // 发送连续6个1来测试位填充检测
            for (i = 0; i < 6; i = i + 1) begin
                wait( req == 1'b1 );
                @(posedge clk);
                tbit = 1'b1;
                can_rx = 1'b1;
                @(posedge clk);
            end
            
            // 发送一个0来结束连续1
            wait( req == 1'b1 );
            @(posedge clk);
            tbit = 1'b0;
            can_rx = 1'b0;
            
            // 发送帧结束
            repeat(8) begin
                wait( req == 1'b1 );
                @(posedge clk);
                tbit = 1'b1;
                can_rx = 1'b1;
                @(posedge clk);
            end
            
            wait( u_dut.inframe == 1'b0 );
            $display("[%0t] 位填充测试完成", $time);
        end
    endtask
    
    // 完成测试
    task finish_test;
        begin
            repeat(50) @(posedge clk);
            
            if (error_count == 0) begin
                $display("[%0t] 所有测试通过！", $time);
            end else begin
                $display("[%0t] 测试失败！错误数: %0d", $time, error_count);
            end
            
            $finish;
        end
    endtask
    
    // 监控信号变化
    always @(posedge clk) begin
        if (req) begin
            $display("[%0t] 采样请求: rbit=%b, tbit=%b", $time, rbit, tbit);
        end
        
        if (u_dut.rx_fall) begin
            $display("[%0t] 检测到RX下降沿", $time);
        end
    end
    
    // 状态监控
    always @(u_dut.stat) begin
        case(u_dut.stat)
            u_dut.STAT_PTS:  $display("[%0t] 状态切换: STAT_PTS", $time);
            u_dut.STAT_PBS1: $display("[%0t] 状态切换: STAT_PBS1", $time);
            u_dut.STAT_PBS2: $display("[%0t] 状态切换: STAT_PBS2", $time);
            default:         $display("[%0t] 状态切换: UNKNOWN", $time);
        endcase
    end
    
    // 超时保护
    initial begin
        #1000000; // 1ms超时
        $display("[%0t] 测试超时！", $time);
        $finish;
    end
    
    // 生成VCD文件用于调试
    initial begin
        $dumpfile("can_level_bit.vcd");
        $dumpvars(0, tb_can_level_bit);
    end

endmodule